﻿using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;

//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2021 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------

namespace shacoEditor
{
    //快速替换名字规则
    public interface IQuickRenameRule
    {
        //ui绘制
        void OnGUI();

        //参数输入是否有效
        bool IsValidParam();

        //开始重命名
        void StartReanme(IEnumerable<string> searchAssetsPath);

        //替换名字方法
        string Rename(string fileName);

        //是否忽略该路径
        bool IsIgnore(string assetPath);
    }

    //替换前缀名
    public class QuickRenameRulePrefixName : IQuickRenameRule
    {
        private bool _isIncludeFolder = false;
        private string _oldPrefixName = string.Empty;
        private string _newPrefixName = string.Empty;

        public void OnGUI()
        {
            _isIncludeFolder = EditorGUILayout.Toggle("Include Folder", _isIncludeFolder);
            _oldPrefixName = EditorGUILayout.TextField("Old Prefix Name", _oldPrefixName);
            _newPrefixName = EditorGUILayout.TextField("New Prefix Name", _newPrefixName);
        }
        
        public bool IsValidParam()
        {
            return !string.IsNullOrEmpty(_newPrefixName) || !string.IsNullOrEmpty(_oldPrefixName);
        }

        public void StartReanme(IEnumerable<string> searchAssetsPath)
        {

        }

        public bool IsIgnore(string assetPath)
        {
            if (!_isIncludeFolder)
                return AssetDatabase.IsValidFolder(assetPath);
            else
                return false;
        }

        public string Rename(string fileName)
        {
            var retValue = fileName;
            if (!string.IsNullOrEmpty(_oldPrefixName))
                retValue = fileName.Replace(_oldPrefixName, _newPrefixName);
            else
            {
                //当原名字已经替换过前缀名了，则避免重复替换
                if (fileName.StartsWith(_newPrefixName))
                    retValue = string.Empty;
                else
                    retValue = _newPrefixName + fileName;
            }
            return retValue;
        }
    }

    //序列格式化数字
    public class QuickRenameRuleQueueNumber : IQuickRenameRule
    {
        private int _startNumber = 1;
        private int _currentNumber = 1;
        private const string DEFAULT_SPLIT_FLAG = "_";

        public bool IsValidParam()
        {
            return true;
        }

        public void OnGUI()
        {
            _startNumber = EditorGUILayout.IntField("Start Number", _startNumber);
        }

        public void StartReanme(IEnumerable<string> searchAssetsPath)
        {
            _currentNumber = _startNumber;
        }

        public bool IsIgnore(string assetPath)
        {
            return false;
        }

        public string Rename(string fileName)
        {
            var extensions = System.IO.Path.GetExtension(fileName);
            var fileNameWithoutExtension = string.IsNullOrEmpty(extensions) ? fileName : System.IO.Path.GetFileNameWithoutExtension(fileName);

            //过滤已经是该下标的文件名
            var nubmerExtension = DEFAULT_SPLIT_FLAG + _currentNumber++;
            if (fileNameWithoutExtension.EndsWith(nubmerExtension))
                return string.Empty;

            //删除末尾空格和数字
            int removeIndex = fileNameWithoutExtension.Length - 1;
            for (; removeIndex >= 0; --removeIndex)
            {
                var cTmp = fileNameWithoutExtension[removeIndex];
                if (cTmp != ' ' && cTmp != '_' && (cTmp < '0' || cTmp > '9'))
                {
                    break;
                }
            }
            if (removeIndex != fileNameWithoutExtension.Length - 1)
                fileNameWithoutExtension = fileNameWithoutExtension.Substring(0, removeIndex + 1);
            var retValue = fileNameWithoutExtension + nubmerExtension + extensions;
            return retValue;
        }
    }

    /// <summary>
    /// 用于unity中快速文件重命名，支持图片、文本、脚本、prefab等
    /// 目前对于脚本不能完全做到编译级识别，以后有时间再优惠吧
    /// </summary>
    public class QuickRenameWindow : EditorWindow
    {
        private class WillMoveFileInfo
        {
            public string oldAssetPath;
            public string newAssetPath;
        }

        private SerializedObject _serializedObject = null;
        [SerializeField]
        private List<DefaultAsset> _searchFolders = new List<DefaultAsset>();
        private SerializedProperty _propertySearchFolders = null;
        [SerializeField]
        private List<string> _searchPatterns = new List<string>() { };
        private SerializedProperty _propertySearchPatterns = null;
        [SerializeField]
        private List<string> _ignoreFileNames = new List<string>() { };
        private SerializedProperty _propertyIgnoreFileNames = null;

        private IQuickRenameRule _quickRenameRule = new QuickRenameRulePrefixName();

        [MenuItem("shaco/Tools/QuickRename", false, (int)ToolsGlobalDefine.MenuPriority.Tools.QUICK_RENAME)]
        static private void OpenWindow()
        {
            var window = (QuickRenameWindow)EditorWindow.GetWindow(typeof(QuickRenameWindow), true);
            window.Init();
            window.Show();
        }

        private void Init()
        {
            var defaultSearchPattern = EditorPrefs.GetString("QUICK_RENAME_DEFAULT_SEARCH_PATTERN", "t:Script");
            _searchPatterns.Clear();
            if (!string.IsNullOrEmpty(defaultSearchPattern))
                _searchPatterns.Add(defaultSearchPattern);

            var defaultIgnoreFileName = EditorPrefs.GetString("QUICK_RENAME_DEFAULT_IGNORE_FILENAME", "Editor");
            _ignoreFileNames.Clear();
            if (!string.IsNullOrEmpty(defaultIgnoreFileName))
                _ignoreFileNames.Add(defaultIgnoreFileName);

            _serializedObject = new SerializedObject(this);
            _propertySearchFolders = _serializedObject.FindProperty("_searchFolders");
            _propertySearchPatterns = _serializedObject.FindProperty("_searchPatterns");
            _propertyIgnoreFileNames = _serializedObject.FindProperty("_ignoreFileNames");
            AutoUpdateSearchFolder();
        }

        void OnDestroy()
        {
            if (_searchPatterns.Count > 0)
                EditorPrefs.SetString("QUICK_RENAME_DEFAULT_SEARCH_PATTERN", _searchPatterns[0]);
            else
                EditorPrefs.SetString("QUICK_RENAME_DEFAULT_SEARCH_PATTERN", string.Empty);

            if (_ignoreFileNames.Count > 0)
                EditorPrefs.SetString("QUICK_RENAME_DEFAULT_IGNORE_FILENAME", _ignoreFileNames[0]);
            else
                EditorPrefs.SetString("QUICK_RENAME_DEFAULT_IGNORE_FILENAME", string.Empty);
        }

        void OnGUI()
        {
            if (null == _serializedObject)
            {
                OpenWindow();
                return;
            }

            GUILayout.BeginVertical("box");
            {
                _quickRenameRule = GUILayoutHelper.PopupTypeField<IQuickRenameRule>("Rename Rule", _quickRenameRule);
                _quickRenameRule.OnGUI();
            }
            GUILayout.EndVertical();

            _serializedObject.Update();
            EditorGUI.BeginChangeCheck();
            {
                AutoUpdateSearchFolder();
                EditorGUILayout.PropertyField(_propertySearchFolders, true);
                EditorGUILayout.PropertyField(_propertySearchPatterns, true);
                EditorGUILayout.PropertyField(_propertyIgnoreFileNames, true);
            }
            if (EditorGUI.EndChangeCheck())
            {
                _serializedObject.ApplyModifiedProperties();
            }

            EditorGUI.BeginDisabledGroup(!_quickRenameRule.IsValidParam());
            {
                if (GUILayout.Button("Start"))
                {
                    var searchResult = GetSearchAssetsGUID();
                    StartRename(searchResult);
                }
            }
            EditorGUI.EndDisabledGroup();
        }

        void AutoUpdateSearchFolder()
        {
            if (null != _searchFolders && 2 <= _searchFolders.Count)
                return;

            this.Repaint();

            if (null == Selection.assetGUIDs || 0 == Selection.assetGUIDs.Length)
                return;

            var assetPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
            if (!AssetDatabase.IsValidFolder(assetPath))
                return;

            var loadedAsset = AssetDatabase.LoadAssetAtPath<DefaultAsset>(assetPath);
            if (null == loadedAsset)
                return;

            bool isChanged = true;
            if (_searchFolders.Count == 0)
                _searchFolders.Add(loadedAsset);
            else
            {
                if (_searchFolders[0] != loadedAsset)
                    _searchFolders[0] = loadedAsset;
                else
                    isChanged = false;
            }

            if (isChanged)
            {
                _serializedObject.ApplyModifiedProperties();
            }
        }

        string[] GetSearchAssetsGUID()
        {
            string[] retValue = null;
            var searchFilter = GetSearchFilter();
            var searchFoldersPath = new List<string>();
            foreach (var iter in _searchFolders)
            {
                if (null == iter)
                    continue;

                var assetPath = AssetDatabase.GetAssetPath(iter);
                if (string.IsNullOrEmpty(assetPath))
                    continue;

                searchFoldersPath.Add(assetPath);
            }

            if (searchFoldersPath.Count > 0)
            {
                retValue = AssetDatabase.FindAssets(searchFilter, searchFoldersPath.ToArray());
            }

            if (null == retValue || 0 == retValue.Length)
            {
                Debug.LogError("QuickRenameWindow StartRename error: no search file or folder");
            }
            return retValue;
        }

        void StartRename(string[] searchAssetsGUID)
        {
            if (null == searchAssetsGUID || 0 == searchAssetsGUID.Length)
                return;

            var searchAssetsPath = searchAssetsGUID.Select((v, index) => AssetDatabase.GUIDToAssetPath(v)).Where(v =>
            {
                foreach (var ignoreName in _ignoreFileNames)
                {
                    if (v.Contains(ignoreName) || _quickRenameRule.IsIgnore(v))
                        return false;
                }
                return true;
            });

            var willRenameFileCount = searchAssetsPath.Count();
            var ignoreFileCount = searchAssetsGUID.Length - willRenameFileCount;
            if (!EditorUtility.DisplayDialog("Confirm", "Rename count:" + willRenameFileCount + "\n Ignore count:" + ignoreFileCount, "OK", "Cancel"))
                return;

            _quickRenameRule.StartReanme(searchAssetsPath);
            var willMoveFilesInfo = new List<WillMoveFileInfo>();
            foreach (var iter in searchAssetsPath)
            {
                var assetPath = iter;
                shaco.Base.FileHelper.FixedToLinuxSplitPath(ref assetPath);
                var folderPath = System.IO.Path.GetDirectoryName(assetPath) + "/";
                var fileName = System.IO.Path.GetFileName(assetPath);

                var newFileName = _quickRenameRule.Rename(fileName);
                var newAssetPath = string.IsNullOrEmpty(newFileName) ? string.Empty : folderPath + newFileName;

                //如果修改的是纯文本，则需要同时修改文本内的内容
                var loadedAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(assetPath);
                if (null != loadedAsset)
                {
                    var oldFileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(fileName);
                    var newFileNameWithoutExtension = string.Empty;
                    newFileNameWithoutExtension = _quickRenameRule.Rename(oldFileNameWithoutExtension);

                    //过滤已经修改过的文件内容前缀
                    if (!string.IsNullOrEmpty(newFileNameWithoutExtension))
                    {
                        ReplaceReference(assetPath, oldFileNameWithoutExtension, newFileNameWithoutExtension);
                        ReplaceAllReference(searchAssetsPath, oldFileNameWithoutExtension, newFileNameWithoutExtension);
                    }
                }

                //过滤已经修改过前缀名的文件路径
                if (!string.IsNullOrEmpty(newAssetPath))
                {
                    willMoveFilesInfo.Add(new WillMoveFileInfo()
                    {
                        oldAssetPath = assetPath,
                        newAssetPath = newAssetPath
                    });
                }
            }

            AssetDatabase.SaveAssets();

            //为了避免名字可能出现冲突，先使用临时文件名
            for (int i = willMoveFilesInfo.Count - 1; i >= 0; --i)
            {
                var iter = willMoveFilesInfo[i];
                AssetDatabase.MoveAsset(iter.oldAssetPath, iter.newAssetPath + "tmp");
            }

            //恢复文件名
            for (int i = willMoveFilesInfo.Count - 1; i >= 0; --i)
            {
                var iter = willMoveFilesInfo[i];
                AssetDatabase.MoveAsset(iter.newAssetPath + "tmp", iter.newAssetPath);
            }


            Debug.Log("Rename success");
        }

        bool ReplaceReference(string assetPath, string oldFileNameWithoutExtension, string newFileNameWithoutExtension)
        {
            if (!System.IO.File.Exists(assetPath))
            {
                Debug.LogError("QuickRenameWindow ReplaceReference error: invalid asset path=" + assetPath);
                return false;
            }

            var loadText = System.IO.File.ReadAllText(assetPath);
            if (loadText.IndexOf(oldFileNameWithoutExtension) < 0 || loadText.IndexOf(newFileNameWithoutExtension) >= 0)
            {
                return false;
            }
            else
            {
                var replacedText = loadText.Replace(oldFileNameWithoutExtension, newFileNameWithoutExtension);
                System.IO.File.WriteAllText(assetPath, replacedText);
            }
            return true;
        }

        void ReplaceAllReference(IEnumerable<string> searchAssetsPath, string oldFileNameWithoutExtension, string newFileNameWithoutExtension)
        {
            foreach (var iter in searchAssetsPath)
            {
                var assetPath = iter;
                var loadedAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(assetPath);
                if (null != loadedAsset)
                {
                    ReplaceReference(assetPath, oldFileNameWithoutExtension, newFileNameWithoutExtension);
                }
                else
                {
                    Debug.LogError("QuickRenameWindow ReplaceAllReference error: not found path=" + assetPath);
                }
            }
        }

        string GetSearchFilter()
        {
            var retValue = new System.Text.StringBuilder();
            foreach (var iter in _searchPatterns)
            {
                if (string.IsNullOrEmpty(iter))
                    continue;

                retValue.Append(iter);
                retValue.Append(' ');
            }

            if (retValue.Length > 0)
                retValue.Remove(retValue.Length - 1, 1);
            return retValue.ToString();
        }
    }
}